/* //device/system/broncho-ril/reference-ril.c
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#include <telephony/ril.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <alloca.h>
#include "atchannel.h"
#include "at_tok.h"
#include "ril_util.h"
#include <getopt.h>
#include "broncho-ril.h"
#include "ril-sim.h"
#include "cutils/properties.h"

#define LOG_TAG "RIL"
#include "broncho_log.h"

#define PLMN_NAME_FORMAT_NR	3
static const struct timeval TIMEVAL_NETPOLL = {1,0};

typedef struct _MmContext
{
	int	usb_power;
}MmContext;

static MmContext* getMmContext(RIL* thiz)
{
	MmContext* ctx = RIL_GetPrivInfo(thiz, "mm_ctx");
	
	if(ctx == NULL)
	{
		ctx = calloc(1, sizeof(MmContext));
		RIL_SetPrivInfo(thiz, ctx, "mm_ctx");
	}

	return ctx;

}

static void sendNetworkStateChanged(void* param)
{
	RIL* thiz = (RIL* )param;

	RIL_onUnsolicitedResponse (thiz, 
			RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED,
			NULL, 0);

}

static void mmSetUsbPower(RIL* thiz, int on)
{
	MmContext* ctx = getMmContext(thiz);
	
	if(ctx->usb_power != on) 
	{
		ctx->usb_power = on;
		RIL_PowerUsbDevice(thiz, on);
	}

	return;
}

static void requestSetLocationUpdates(RIL* thiz, void* data, size_t datalen, RIL_Token t)
{
	int enable = ((int*)data)[0];
	char* cmd = NULL;

	/*TODO: */

	RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);

	return;
}

static void requestCallGpsone(RIL* thiz, void* data, size_t datalen, RIL_Token t)
{
	if(RIL_getSIMStatus(thiz) != RIL_SIM_READY)
	{
		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
		return;
	}

	if(ril_command_exec_for_result(thiz, "AT+GPSONE"))
	{
		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
	}
	else
	{
		RIL_onRequestComplete(thiz, t, RIL_E_GENERIC_FAILURE, NULL, 0);
	}
	return;
}

static void requestSetGps(RIL* thiz, void* data, size_t datalen, RIL_Token t)
{
	int enable = 0;
	char cmd[32];
	
	enable = ((int*)data)[0];

	if(enable)
	{
		snprintf(cmd, 32, "AT+GPSSTART");
	}
	else
	{
		snprintf(cmd, 32, "AT+GPSSTOP");
	}

	if(ril_command_exec_for_result(thiz, cmd))
	{
		if(enable) 
		{
			property_set("system.gps.device", RIL_GetGpsChannelDeviceName(thiz));
			mmSetUsbPower(thiz, 1);
		} 
		else 
		{
			mmSetUsbPower(thiz, 0);
		}
		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
	}
	else
	{
		RIL_onRequestComplete(thiz, t, RIL_E_GENERIC_FAILURE, NULL, 0);
	}

	return;
}

static void requestScreenState(RIL* thiz, void* data, size_t datalen, RIL_Token t)
{
	int state = 0;
	char cmd[32] = {0};
	
	state = ((int*)data)[0];
	if(state)
	{
		snprintf(cmd, 32, "AT+REPORTMASK=1,0,0,1");
	}
	else
	{
		snprintf(cmd, 32, "AT+REPORTMASK=0,0,0,0");
	}
	
	if(ril_command_exec_for_result(thiz, cmd))
	{
		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
	}
	else
	{
		RIL_onRequestComplete(thiz, t, RIL_E_GENERIC_FAILURE, NULL, 0);
	}

	return;
}

static void requestSignalStrength(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
    ATResponse *p_resp = NULL;
    int err;
    int response[7] = {0};
	int ignore;
	int success = 0;

	//RIL_requestTimedCallback(thiz, sendNetworkStateChanged, thiz, &TIMEVAL_NETPOLL);

	do
	{
    	if(ril_command_exec_singleline_for_result(thiz, "AT+CSQ", "+CSQ:", &p_resp))
		{
			at_line_scanf_int(p_resp->p_intermediates->line, &response[2]);

			if(response[2] == 0 || response[2] == 99)
			{
				response[2] = 125;
				response[3] = 210;
			}
			else if(response[2] >= 31)
			{
				response[2] = 75;
				response[3] = 90;
			}
			else
			{
				response[3] = 210 - 120 * response[2] / 31;
				response[2] = 125 - 50 * response[2] / 31;

				/*add val*/
				/*TODO: */
				//response[2] = (response[2] < 110) ? (response[2] - 20) : response[2];
				response[2] = (response[2] < 75) ? 75 : response[2];
				//response[3] = (response[3] < 190) ? (response[2] - 40) : response[2];
				response[3] = (response[3] < 90) ? 90 : response[3];
			}
			at_response_free(p_resp);
			success = 1;
		}
		
		/*
		if(ril_command_exec_singleline_for_result(thiz, "AT^HDRINFO", "^HDRINFO:", &p_resp))
		{
			at_line_scanf_int_int_int_int_int_int_int_int(p_resp->p_intermediates->line, 
			 	&ignore, &ignore, &ignore, &ignore, &ignore, &ignore, &response[5], &response[6]);
			at_response_free(p_resp);
		}
	    
		if(ril_command_exec_singleline_for_result(thiz, "AT+RFRSSI", "+RFRSSI:", &p_resp))
		{
			at_line_scanf_int(p_resp->p_intermediates->line, &response[4]);
			response[5] = response[4] * 10;
			ALOGD("###EVDO DBM####=%d", response[4]);
			at_response_free(p_resp);
		}
		*/
		/*TODO: fixme */
		if(ril_command_exec_singleline_for_result(thiz, "AT^HDRCSQ", "^HDRCSQ:", &p_resp))
		{
			int hdr_rssi = 0;
			at_line_scanf_int(p_resp->p_intermediates->line, &hdr_rssi);
			switch(hdr_rssi)
			{
			case 0:
				response[6] = 0;
				response[4] = 130;
				break;
			case 20:
				response[6] = 2;
				response[4] = 105;
				break;
			case 40:
				response[6] = 4;
				response[4] = 90;
				break;
			case 60:
				response[6] = 6;
				response[4] = 75;
				break;
			case 80:
				response[6] = 8;
				response[4] = 60;
				break;
			default:
				response[6] = 8;
				response[4] = 50;
				break;
			}
			response[5] = response[4] * 10;
		}

	}while(0);

	if (success)
	{
		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, response, sizeof(response));
	}
	else
	{
		RIL_onRequestComplete(thiz, t, RIL_E_GENERIC_FAILURE, NULL, 0);
		ALOGE("requestSignalStrength must never return an error when radio is on");
	}

    return;
}

static void requestRegistrationState(RIL* thiz, int request, void *data,
                                        size_t datalen, RIL_Token t)
{
	char* response[14] = {0};
	ATResponse* p_sysinfo_resp = NULL;
	ATResponse* p_roaminfo_resp = NULL;
	ATResponse* p_1xinfo_resp = NULL;
	int ignore = 0;
	char* ignoring = NULL;
	int success = 0;
	int status = 0;
	int domain = 0;
	int roam = 0;
	int sys_mode = 0;
	int ruimState = 0;

	do
	{
		if(!ril_command_exec_singleline_for_result(thiz, "AT^SYSINFO", "^SYSINFO:", &p_sysinfo_resp))
		{
			break;
		}
			
		at_line_scanf_int_int_int_int_int(p_sysinfo_resp->p_intermediates->line,
			&status, &domain, &roam, &sys_mode, &ruimState);

		if(ruimState == 1)
		{
			if(status == 2)
			{
				response[0] = (roam == 0) ? "1" : "5";
			}
			else if(status == 4)
			{
				response[0] = "2";
			}
		}
		else
		{
			response[0] = "0";
		}
		
		/*invalid field*/
		response[1] = "-1";
		response[2] = "-1";

		/*data connection type. evdo or 1x***/
		if(sys_mode == 4 || sys_mode == 8)
		{
			response[3] = "8";
		}
		else if(sys_mode == 2)
		{
			response[3] = "6";
		}
		else
		{
			response[3] = "0";
		}
		
		/*baseStationLatitude baseStationLongitude cssIndicator*/
		response[5] = "-1";
		response[6] = "-1";
		response[7] = "0";
		
		/*TODO: */
		/*
		if(!ril_command_exec_singleline_for_result(thiz, "AT^1XINFO", "^1XINFO:", &p_1xinfo_resp))
		{
			break;
		}
		at_line_scanf_string_string_string_string_string_string(p_1xinfo_resp->p_intermediates->line,
					&ignoring, &ignoring, &response[4], &response[8], &response[9], &ignoring);
		ALOGD("1xinfo: line=%s, bid=%s, sid=%s, nid=%s\n", 
				p_1xinfo_resp->p_intermediates->line, response[4], response[8], response[9]);
		*/
		response[4] = "-1";
		response[8] = "-1";
		response[9] = "-1";
		

		if(!ril_command_exec_singleline_for_result(thiz, "AT^ROAMINFO", "^ROAMINFO:", &p_roaminfo_resp))
		{
			/*broncho wupeng add, temp solution.*/
			success = 1;
			break;
		}
		at_line_scanf_string_string_string(p_roaminfo_resp->p_intermediates->line,
				&response[10], &response[11], &response[12]);
        if(response[10][0] == '0')
        {
            response[10] = "1";
        }
        else if(response[10][0] == '1')
        {
            response[10] = "0";
        }
        if(response[12][0] == '0')
        {
            response[12] = "1";
        }
        else if(response[12][0] == '1')
        {
            response[12] = "0";
        }
		ALOGD("roaminfo: line=%s, roam=%s, sysinprl=%s, default_roam=%s\n", 
				p_roaminfo_resp->p_intermediates->line, response[10], response[11], response[12]);
		success = 1;
	}while(0);

	if(success)
	{
		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, response, sizeof(response));
	}
	else
	{
		RIL_onRequestComplete(thiz, t, RIL_E_GENERIC_FAILURE, NULL, 0);
	}
	at_response_free(p_sysinfo_resp);
	at_response_free(p_roaminfo_resp);
	at_response_free(p_1xinfo_resp);

	return;
}

static void requestOperator(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
	int success = 1;
    char *response[3] = {0};
	ATResponse* p_resp = NULL;
	
	/*
    if (ril_command_exec_singleline_for_result(thiz, "AT+CIMI", "+CIMI:", &p_resp))
	{
		char* imsi = NULL;
		char operator[10];

		response[0] = "";
		response[1] = "";
		response[2] = operator;

		at_line_scanf_string(p_resp->p_intermediates->line, &imsi);
		memcpy(operator, imsi, strlen(imsi) - 10);
		operator[strlen(imsi) - 10] = '\0';

		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, response, sizeof(response));
		at_response_free(p_resp);
    }
	*/
	/*if (ril_command_exec_singleline_for_result(thiz, "AT+COPS", "+COPS:", &p_resp))
	{
		at_line_scanf_string(p_resp->p_intermediates->line, &response[2]);
		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, response, sizeof(response));
		at_response_free(p_resp);
	}*/

	success = (ril_command_exec_singleline_for_result(thiz, "AT+COPS?", "+COPS:", &p_resp) == 1);
	//compatible with old modem 6085.
	if(!success)
	{
		success = (ril_command_exec_singleline_for_result(thiz, "AT+COPS", "+COPS:", &p_resp) == 1);
	}
	
	if(success)
	{
		at_line_scanf_string(p_resp->p_intermediates->line, &response[2]);
		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, response, sizeof(response));
		at_response_free(p_resp);
	}
	else 
	{
		RIL_onRequestComplete(thiz, t, RIL_E_GENERIC_FAILURE, NULL, 0);
    }
	
	return;
}

int RIL_initialize_mm (RIL* thiz)
{
	ril_command_exec_for_result(thiz, "AT+REPORTMASK=1,0,0,1");

	return 1;
}

static void onUnsolicited_CsqLevel(RIL* thiz, const char* s)
{
	int level = 0;
	int response[7] = {0};


	if(at_line_scanf_int(s, &level))
	{
		switch(level)
		{
		case 0:
			response[2] = 125;
			response[3] = 180;
			response[6] = 0;
			response[4] = 130;
			break;

		case 1:
			response[2] = 115;
			response[3] = 160;
			response[6] = 2;
			response[4] = 105;
			break;

		case 2:
			response[2] = 105;
			response[3] = 105;
			response[6] = 4;
			response[4] = 90;
			break;

		case 3:
			response[2] = 85;
			response[3] = 95;
			response[6] = 6;
			response[4] = 75;
			break;
		
		default:
			response[2] = 75;
			response[3] = 90;
			response[6] = 8;
			response[4] = 60;
			break;
		}
		response[5] = response[4] * 10;
		RIL_onUnsolicitedResponse(thiz, RIL_UNSOL_SIGNAL_STRENGTH, response, sizeof(response));
	}

	return;
}

static void onUnsolicited_Nitz(RIL* thiz, const char* s)
{
	char* response = NULL;
	char* line = NULL;
	char* dump_s = NULL;
	int err = 0;

	dump_s = strdup(s);
	line = dump_s;
	at_tok_start(&line);
	err = at_tok_nextstr(&line, &response);
	if (err == 0) 
	{
		RIL_onUnsolicitedResponse(thiz, RIL_UNSOL_NITZ_TIME_RECEIVED, response, strlen(response));
	}
	free (dump_s);

	return;
}

int RIL_onUnsolicited_mm (RIL* thiz, const char* s)
{
	int ret = 1;

	if (strStartsWith (s, "^CNETS:"))
	{
		RIL_onUnsolicitedResponse(thiz, RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED, NULL, 0);
	}
	else if (strStartsWith (s, "^CSQLVL:"))
	{
		onUnsolicited_CsqLevel(thiz, s);
	}
	else if (strStartsWith (s, "+NITZ:"))
	{
		onUnsolicited_Nitz(thiz, s);
	}
	else
	{
		ret = 0;
	}
	
	return ret;
}

int RIL_onRequest_mm (RIL* thiz, int request, void *data, size_t datalen, RIL_Token t)
{
	switch (request)
	{
	case RIL_REQUEST_SIGNAL_STRENGTH:
		requestSignalStrength(thiz, data, datalen, t);
		break;

	case RIL_REQUEST_REGISTRATION_STATE:
		requestRegistrationState(thiz, request, data, datalen, t);
		break;

	case RIL_REQUEST_OPERATOR:
		requestOperator(thiz, data, datalen, t);
		break;

	case RIL_REQUEST_SCREEN_STATE:
		requestScreenState(thiz, data, datalen, t);
		break;

	case RIL_REQUEST_SET_LOCATION_UPDATES:
		requestSetLocationUpdates(thiz, data, datalen, t);
		break;

	case RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE:
		/*TODO: */	
		RIL_onRequestComplete(thiz, t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
		break;

	case RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE:
		/*TODO: */
		RIL_onRequestComplete(thiz, t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
		break;

	case RIL_REQUEST_CALL_GPSONE:
		requestCallGpsone(thiz, data, datalen, t);
		break;

	case RIL_REQUEST_SET_GPS:
		requestSetGps(thiz, data, datalen, t);
		break;

	case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE:
	case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC:
	case RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL:
	case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS:
	case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE:
	case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE:
	case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS:
		RIL_onRequestComplete(thiz, t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
		break;
	
	default:
		RIL_onRequestComplete(thiz, t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
		break;
	}

	return 1;
}


